#include <string>
#include <algorithm>
#include <iostream>
#include <vector>
#include <deque>
#include <sstream>
#include <boost/lexical_cast.hpp>
#include <algorithm>
#include <stdlib.h>
#include "stlhelper.h"
#include "dynamic_matrix.h"
#include "portable_timer.h"
#include "progress_indicator.h"
#include "pg_copy_guard.h"
#include "pg_db_cursor.h"
#include "index_string.h"
//# define DEBUG_LEVEL 19
//# define DEBUG_LEVEL 19
#include "debug_func.h"
#include "get_arg.h"
using std::cerr;
using std::sort;
using std::cout;
using std::string;
using std::deque;


bool print_gene_pair_ks(		t_pg_cursor&					pg_db,
								t_program_args&					args,
								const string& 					ks_table,
								const t_dynamic_matrix_bool&	bool_matrix,
								t_index_string&					gene_names_index,
								double							ks_cutoff,
								unsigned&						cnt_pairs,
								unsigned&						cnt_success,
								unsigned&						cnt_less_than,
								unsigned&						cnt_genes)
{
	debug_func_cerr( 8 );
	args.VERBOSE() << "\tRetrieving and printing Ks from [" << ks_table << "]...\n";
	string sql_cmd = "SELECT "
							"gene_id1, gene_id2, ks "
					 "FROM "
							+ ks_table;
	

	
	deque<unsigned> genes;
	
	// create binary cursor
	if (!pg_db.Declare(sql_cmd, true))
		return pg_print_sql_err("create a cursor to retrieve value and gene pairs",
								pg_db,
								sql_cmd, args.CERR());
	
	{
		t_progress_indicator	dots (args.VERBOSE(), 100000);
		unsigned batch_size = 5000;
		// get data in batches
		string gene_id1;
		string gene_id2;
		string str_val;
		while (pg_db.Fetch(batch_size))
		{
			int curr_batch_size = pg_db.Tuples();
			if (!curr_batch_size)
				break;
			for (int i = 0; i < curr_batch_size; ++i)
			{
				++cnt_pairs;
				++dots;
				
				float val = pg_db.getvalue<float>(i, 2);
				if (val > ks_cutoff)
					continue;

				++cnt_less_than;
				
				
				// print out gene_ids if match previous filter
				gene_id1 = pg_db.getvalue(i, 0);
				gene_id2 = pg_db.getvalue(i, 1);
				unsigned gi1 = gene_names_index.index(gene_id1);
				unsigned gi2 = gene_names_index.index(gene_id2);
				if (gi1 > gi2)
					std::swap(gi1,  gi2);
				if (bool_matrix.at(gi1, gi2))
				{
					std::cout	<< gene_id1 << "\t" << gene_id2 << "\t"
								<< val << "\n";
					cnt_success++;
					genes.push_back(gi1);
					genes.push_back(gi2);
				}
			 }
	
		}
	}

	sort(genes.begin(),  genes.end());
	remove_duplicates(genes);
	cnt_genes = genes.size();
	
	// close cursor
	if (!pg_db.Close())
		return pg_print_sql_err("close the  cursor for "
								"create a cursor to retrieve ks and gene pairs",
								pg_db, "CLOSE CURSOR", args.CERR());
	
	return true;
	
}





	

bool get_filtered_gene_pair(	t_pg_cursor&			pg_db,
								t_program_args&			args,
								const string& 			table,
								const string& 			column,
								t_dynamic_matrix_bool&	bool_matrix,
								double					cutoff,
								bool					less_than,
								t_index_string&			gene_names_index,
								unsigned&				cnt_filtered,
								unsigned&				cnt_duplicates)
{
	debug_func_cerr( 8 );
	args.VERBOSE() << "\tRetrieving values from the filter table [" << table << "] ...\n";
	string sql_cmd = "SELECT "
							"gene_id1, gene_id2, " + column +
					 " FROM "
							+ table;
	
	// create binary cursor
	if (!pg_db.Declare(sql_cmd, true))
		return pg_print_sql_err("create a cursor to retrieve value and gene pairs",
								pg_db,
								sql_cmd, args.CERR());
	
	{
		t_progress_indicator	dots (args.VERBOSE(), 100000);
		unsigned batch_size = 5000;
		// get data in batches
		while (pg_db.Fetch(batch_size))
		{
			int curr_batch_size = pg_db.Tuples();
			if (!curr_batch_size)
				break;
			for (int i = 0; i < curr_batch_size; ++i)
			{
				++dots;
				float val = pg_db.getvalue<float>(i, 2);
				if (less_than)
				{
					if (val > cutoff)
						continue;
				}
				else
				{
					if (val < cutoff)
						continue;
				}
				// save gi and sequence etc in deque which grows efficiently
				unsigned gi1 = gene_names_index.index(pg_db.getvalue(i, 0));
				unsigned gi2 = gene_names_index.index(pg_db.getvalue(i, 1));
				if (gi1 > gi2)
					std::swap(gi1,  gi2);
				if (!bool_matrix.at(gi1, gi2))
				{
					bool_matrix.set(gi1, gi2, true);
					cnt_filtered++;
				}
				else
				{
					cnt_duplicates++;
				}
			 }
	
		}
	
	}
	
	// close cursor
	if (!pg_db.Close())
		return pg_print_sql_err("close the  cursor for "
								"create a cursor to retrieve ks and gene pairs",
								pg_db, "CLOSE CURSOR", args.CERR());
	
	return true;
}



//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

//		Main

//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
int main(int argc, char* argv[])
{
	t_portable_timer timer;
	
	// don't use C streams
	std::ios::sync_with_stdio(false);

	// saving parameters
	string exe_parameters;
	for (int i = 0; i < argc; ++i)
		exe_parameters += string(argv[i]) + ' ';

	//buffer cout
    char buff[65536];
    cout.rdbuf()->pubsetbuf(buff, 65536); // enable buffering

	// get command line arguments
	t_program_args args;
	if (!GetArguments(argc, argv, args))
		return 1;

	
	//____________________________________________________________________________________
	// 		Connect to PostgreSQL
	//____________________________________________________________________________________
	t_pg_cursor pg_db(args.db_login.c_str(), "panda_cursor");	//"dbname=all_vs_all_gene_bl2seq user=postgres"
	if ( pg_db.ConnectionBad() )
	{
		cerr	<< "\nERROR!! Connection to database ["
							<< args.db_login << "] failed.\n"
							<< pg_db.ErrorMessage() << "\n\n";
		return 1;
	}
	
	
	//____________________________________________________________________________________
	// 		get pairs to filter
	//____________________________________________________________________________________
	t_index_string gene_names(500000);
	t_dynamic_matrix_bool	bool_matrix(false);
	unsigned cnt_filtered = 0;
	unsigned cnt_duplicates = 0;
	if (!get_filtered_gene_pair(	pg_db, args, args.filter_table,
									args.filter_column, bool_matrix,
									args.filter_cutoff,
									args.filter_less_than, gene_names,
									cnt_filtered, cnt_duplicates))
		return 1;

	
	//____________________________________________________________________________________
	// 		print ks pairs meeting criteria
	//____________________________________________________________________________________
	unsigned cnt_pairs = 0;
	unsigned cnt_success = 0;
	unsigned cnt_less_than = 0;
	unsigned cnt_genes = 0;
	if ( !print_gene_pair_ks(	pg_db, args, args.ks_table,
								bool_matrix, gene_names,
								args.ks_cutoff, cnt_pairs,
								cnt_success, cnt_less_than,
								cnt_genes))
		
		return 1;

	
	args.VERBOSE() << string(80, '_') << "\n";
	args.VERBOSE() << cnt_filtered << " pairs passed the filter with " << cnt_duplicates << " duplicates.\n";
	args.VERBOSE() << cnt_less_than << " pairs out of " << cnt_pairs << " with ks less than " << args.ks_cutoff << "\n";
	args.VERBOSE() << "...\t resulting in " << cnt_success << " pairs.\n";
	args.VERBOSE() << cnt_genes << " out of " << gene_names.size() << " genes .\n";
	
	args.VERBOSE() << timer.pretty_reset() << " Seconds\n";

	return 0;
}





